Make this Notebook Trusted to load map: File -> Trust Notebook
Interactive map
Interactive Map: Addresses and Parcels in The Fan
Below is an interactive map of the Fan District Association. You can zoom and scroll.
Below is an interactive map of the Fan District Association. You can zoom and scroll.
---
title: Interactive map
---
```{python}
import geopandas as gpd
import os
import matplotlib as plt
# List of base names
base_names = ["Fan District Association","Civic_Associations", "Addresses_in_fan", "Parcels_in_fan"]
# Folder where GeoJSON files are located
geojson_folder = "../precious/"
features = ["Addresses","Parcels"]
selector = "Civic_Associations"
selector_key = "Fan District Association"
input_folder = "../data"
# Load files into a dictionary
data = {}
for name in base_names:
file_path = os.path.join(input_folder, f"{name}.geojson")
if os.path.exists(file_path):
data[name] = gpd.read_file(file_path)
else:
print(f"⚠️ File not found: {file_path}")
```
## Interactive Map: Addresses and Parcels in The Fan
Below is an interactive map of the Fan District Association. You can
zoom and scroll.
```{python}
import folium
from shapely.geometry import mapping
import geopandas as gpd
# Project and compute bounding box in WGS84
fan_shape = data[selector_key].to_crs(epsg=4326) # Folium uses WGS84
# Compute bounds: [[south, west], [north, east]]
minx, miny, maxx, maxy = fan_shape.total_bounds
bounds = [[miny, minx], [maxy, maxx]]
# Center for initial rendering (optional fallback)
center = [(miny + maxy) / 2, (minx + maxx) / 2]
# Create map and set bounds
m = folium.Map(location=center, zoom_start=15, tiles="cartodbpositron")
m.fit_bounds(bounds)
```
```{python}
# Add neighborhood boundary
x = folium.GeoJson(
data[selector_key].geometry,
name="The Fan Boundary",
style_function=lambda x: {
"color": "black",
"weight": 3,
"fillOpacity": 0,
}
).add_to(m)
```
```{python}
# Add parcels (lighter gray polygons)
x = folium.GeoJson(
data["Parcels_in_fan"].geometry,
name="Parcels",
style_function=lambda x: {
"color": "#999999",
"weight": 0.5,
"fillOpacity": 0.4,
},
).add_to(m)
# Add addresses (red points)
```
```{python}
from shapely.geometry import Point
def get_color_for_unit_count(unit_count: int, max_unit_count: int = 10) -> str:
if unit_count == 1:
return "#FF0000" # red
# For unit_count > 1: magenta to white gradient
scale = min(unit_count, max_unit_count) / max_unit_count
green_value = int(255 * scale) # from 0 (magenta) to 255 (white)
return f"#FF{green_value:02X}FF"
for _, row in data["Addresses_in_fan"].iterrows():
pt = row.geometry
# Skip labels for addresses where UnitType is not None.
unit_type = row["UnitType"]
if unit_type is not None:
continue
# Only plot if geometry is a valid Point
if not isinstance(pt, Point):
print(f"Skipping non-Point geometry at index {_}: {type(pt)}")
continue
if (row.get("BuildingNumber")=="1465") and (row.get("StreetName")=="Floyd"):
#logger.debug( row )
pass
unit_count = row.get("UnitCount",1)
label = row.get("AddressLabel", "")
if unit_count>1:
label = label + f" ({unit_count})"
tooltip = folium.Tooltip(label) if label.strip() else None
color = get_color_for_unit_count(unit_count)
folium.CircleMarker(
location=[pt.y, pt.x],
radius=2,
color=color,
fill=True,
fill_opacity=0.8,
tooltip=tooltip, # Explicit safe wrapper
).add_to(m)
# Add layer control and display map
x = folium.LayerControl().add_to(m)
```
::: {.column-page-inset-right}
```{python}
#| fig-height: 10
#| fig-width: 12
m
```
:::